Skip to content

Always use LF line endings in scaffolded code - #39601

Open
ajcvickers wants to merge 1 commit into
dotnet:mainfrom
ajcvickers:scaffold-lf-line-endings
Open

ajcvickers wants to merge 1 commit into
dotnet:mainfrom
ajcvickers:scaffold-lf-line-endings

Conversation

@ajcvickers

Copy link
Copy Markdown
Contributor

Fixes #29948

Code produced by dotnet ef dbcontext scaffold had mixed line endings on non-Windows platforms: the pre-processed T4 templates baked CRLF into their string literals, while WriteLine and the CSharpHelper fluent-chain/XML-comment output used Environment.NewLine. Blank lines (from WriteLine("")) were LF on Linux/macOS and everything else CRLF. This PR makes scaffolded code always use LF, on every platform, by emitting literal LF rather than parsing and replacing the output afterwards.

Changes

  • Templates and pre-processed output: CSharpDbContextGenerator / CSharpEntityTypeGenerator (.tt and generated .cs) use literal LF. The base-class Environment.NewLine uses are replaced, and the templates no longer call WriteLine("") (it is implemented by the Mono template base class for custom templates and uses the platform newline); they use Write("\n") instead.
  • CSharpHelper: Fragment (both overloads) and XmlComment now use a protected virtual NewLine (default Environment.NewLine) instead of IndentedStringBuilder. Output is identical under the default.
  • ScaffoldingCSharpHelper: overrides NewLine to "\n". LfCSharpHelperServiceProvider substitutes it for the default ICSharpHelper for the templates, in both CSharpModelGenerator and TextTemplatingModelGenerator. A replaced or subclassed ICSharpHelper is left alone. Migrations, snapshots and compiled models keep using Environment.NewLine.
  • Pinning: the four template files are eol=lf in .gitattributes and .editorconfig. This matters because the generated DbContext code contains a verbatim string (#warning ...) with a real line break whose value depends on the file's line endings. The PatchTemplate task in EFCore.Templates now writes LF, so the shipped .t4 files are LF regardless of the build machine.

Tests

  • New end-to-end test that scaffolds a model (comments, fluent chains, many-to-many, sequence, index filter) with both CSharpModelGenerator and TextTemplatingModelGenerator, with and without data annotations, and asserts there is no \r.
  • New CSharpHelper tests: exact LF output for the scaffolding helper, and a platform-independent test of the NewLine hook using a CRLF subclass.
  • AssertFileContents (about 75 scaffolding tests) no longer ignores line-ending differences, and AssertContains no longer converts the expected text to Environment.NewLine.

Notes

  • Behavior change: scaffolded files on Windows are now LF rather than CRLF. Re-scaffolding over CRLF files will rewrite every line.
  • The generated .cs files were edited to match what the pre-processor emits, rather than regenerated, to avoid unrelated churn from a different pre-processor version. Regenerating in Visual Studio will bring Environment.NewLine back in the generated base class; the new tests catch that on Windows.
  • Users' own custom .t4 templates keep whatever line endings the template file has.
  • Not changed: CSharpRuntimeModelCodeGenerator (compiled models) and migrations.

🤖 Generated with Claude Code

The pre-processed T4 templates baked CRLF into their string literals, while
WriteLine and the CSharpHelper fluent-chain/XML-comment output used
Environment.NewLine, so scaffolded files had mixed line endings on
Linux/macOS (and the output depended on the platform).

Scaffolded code now always uses LF:
- The templates and their pre-processed output use literal LF, and no longer
  call WriteLine (which uses the platform newline in the Mono template base).
- CSharpHelper gets a protected virtual NewLine used by Fragment and XmlComment.
  Scaffolding uses ScaffoldingCSharpHelper (LF), substituted for the default
  ICSharpHelper for both CSharpModelGenerator and TextTemplatingModelGenerator.
  Migrations, snapshots and compiled models are unchanged.
- The four template files are pinned to LF in .gitattributes/.editorconfig, and
  the shipped templates are patched with LF.

Fixes dotnet#29948

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@ajcvickers
ajcvickers requested a review from a team as a code owner November 11, 2026 10:38
Copilot AI balanced review requested due to automatic review settings November 11, 2026 10:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

dotnet ef dbcontext scaffold on Mac creates line ending differences

2 participants